﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NGinnBPM.Lib.Data;
using System.Runtime.Serialization;

namespace NGinnBPM.Lib.Schema
{
    [Serializable]
    [DataContract]
    public class Flow : IHaveExtensionProperties
    {
        private NetNode _from;
        private NetNode _to;

        [DataMember(IsRequired = false, EmitDefaultValue = false)]
        public string Label { get; set; }

        [IgnoreDataMember]
        public NetNode From
        {
            get { return _from; }
            set { _from = value; IdFrom = _from != null ? _from.Id : null; }
        }

        [IgnoreDataMember]
        public NetNode To
        {
            get { return _to; }
            set { _to = value; IdTo = _to != null ? _to.Id : null; }
        }

        
        [DataMember]
        public string IdFrom { get; set; }

        [DataMember]
        public string IdTo { get; set; }

        [DataMember(IsRequired = false, EmitDefaultValue = false)]
        public TaskOutPortType SourcePort {get; set;}

        [DataMember(IsRequired = false, EmitDefaultValue = false)]
        public int EvalOrder { get; set; }

        [DataMember(IsRequired = false, EmitDefaultValue = false)]
        public string InputCondition { get; set; }
        
        /// <summary>
        /// Cancelling flow. Must start in a task and end in a place.
        /// Upon completion of the task will remove all tokens from its target place,
        /// cancelling all active tasks that were enabled by the tokens.
        /// </summary>
        [DataMember(IsRequired = false, EmitDefaultValue = false)]
        public bool IsCancelling { get; set; }

        public virtual bool Validate(IList<ValidationMessage> msgs)
        {
            int cnt = msgs.Count;
            if (From == null || To == null)
            {
                msgs.Add(new ValidationMessage(true, "Missing flow start or end node", this));
                return false;
            }
            if (SourcePort != TaskOutPortType.Default && From is Place)
            {
                msgs.Add(new ValidationMessage(true, "Flows originating in a place cannot have a source port", this));
                return false;
            }
            if (InputCondition != null && InputCondition.Length > 0 && From is Place)
            {
                msgs.Add(new ValidationMessage(true, "Flows originating in a place cannot have an input condition", this));
            }
            if (SourcePort != TaskOutPortType.Default)
            {
                if (InputCondition != null && InputCondition.Length > 0)
                {
                    msgs.Add(new ValidationMessage(true, "Input condition not allowed for flows from other than default output ports", this));
                }
            }
            if (IsCancelling)
            {
                if (To is Task)
                    msgs.Add(new ValidationMessage(true, "Cancelling flow can lead only to a place", this));
                if (To is Place && ((Place)To).PlaceType == PlaceTypes.End)
                    msgs.Add(new ValidationMessage(true, "Cancelling flow cannot lead to an end place", this));
            }
            for (int i = cnt; i < msgs.Count; i++)
                if (msgs[i].IsError) return false;
            return true;
        }

        public override string ToString()
        {
            return string.Format("{0}->{1}", IdFrom, IdTo);
        }

        #region IHaveExtensionProperties Members

        [DataMember(IsRequired = false, EmitDefaultValue = false)]
        public Dictionary<string, string> ExtensionProperties { get; set; }

        public IEnumerable<string> GetExtensionProperties(string xmlns)
        {
            return ExtensionPropertyHelper.GetExtensionProperties(ExtensionProperties, xmlns);
        }

        public string GetExtensionProperty(string xmlns, string name)
        {
            return ExtensionPropertyHelper.GetExtensionProperty(ExtensionProperties, xmlns, name);
        }

        public void SetExtensionProperty(string xmlns, string name, string value)
        {
            if (ExtensionProperties == null) ExtensionProperties = new Dictionary<string, string>();
            ExtensionPropertyHelper.SetExtensionProperty(ExtensionProperties, xmlns, name, value);
        }

        public string GetExtensionProperty(string fullName)
        {
            return ExtensionPropertyHelper.GetExtensionProperty(ExtensionProperties, fullName);
        }

        #endregion
    }
}
